<?php
namespace  App\Wolf\Pay;

class AlipayApp 
{

    //支付宝网关地址
    public $gateway_url = "https://openapi.alipay.com/gateway.do";

    //支付宝公钥
    public $alipay_public_key = '';

    //商户私钥
    public $private_key = '';

    //应用id
    public $appid = '';

    //编码格式
    public $charset = "UTF-8";

    public $token = NULL;
    
    //返回数据格式
    public $format = "json";

    //签名方式
    public $signtype = "RSA"; //默认  可以为RSA2

    //同步回调
    public $return_url = '';
    //异步回调
    public $notify_url = '';

    // 业务参数集合
    public $setBizContent = '';

    //超时时间
    public $timeout_express = "10m";

    // 产品标示码，固定值：QUICK_WAP_PAY
    public $productCode = 'QUICK_WAP_PAY';

    //api版本
    public $apiVersion = "1.0";

    // 表单提交字符集编码
    public $postCharset = "UTF-8";

    protected $method = "alipay.trade.wap.pay";

    //加密密钥和类型
    public $encryptKey;

    public $encryptType = "AES"; //默认

    protected $alipaySdkVersion = "alipay-sdk-php-20161101";

    protected $product_code = 'QUICK_MSECURITY_PAY';

    /**
     * 设置参数
     * @param [arr] $config [配置参数 必须包含 字段为：]
     * @param public_key    支付宝公钥
     * @param private_key   商户私钥
     * @param appid         应用appid
     * @param notify_url    异步回调
     */
    public function __construct($config) {
        //dd($config);
        $this->alipay_public_key = $config['public_key'];
        $this->private_key       = $config['private_key'];
        $this->appid             = $config['appid'];
        $this->notify_url        = $config['notify_url'];

        isset($config['sign_type']) && $this->notify_url = $config['sign_type'];
        
        if (count($config) < 4 ) {
            echo "配置参数缺失";
            die;
        } 
    }
    
    /**
     * 生成支付宝app签名
     * @return [type] [description]
     */
    public function getSign($data) 
    {
        //组合业务参数生成 bizcontent
        $bizcontent = $this->setBizcontent($data);
        //将参数封装到 变量数组中
        $params     = $this->setAttributes($bizcontent, $this->signType);
        //排序
        ksort($params);
        //生成签名
        $params['sign'] = $this->sign($params, $this->signType);

        return http_build_query($params);        
    }


    /**
     * 支付回调验证方法
     * @param  [type] $data [description]
     * @return [type]       [description]
     */
    public function notifyCheck ($data) 
    {
        $sign = $params['sign'];
        $params['sign_type'] = null; // 
        $params['sign']      = null;
        //排序
        ksort($params);

        $signContent  = $this->getSignContent($params);

        return $this->verify($signContent, $sign, $this->alipay_public_key, $this->signType);

    }



    /**
     * 验签
     * 根据公钥讲返回参数处理 
     * @param  [type] $data                 [处理过的放回数组]
     * @param  [type] $sign                 [返回的签名值]
     * @param  [type] $rsaPublicKeyFilePath [公钥]
     * @param  string $signType             [加密方法]
     * @return [type]                       [结果 bool]
     */
    private function verify($data, $sign, $rsaPublicKeyFilePath, $signType = 'RSA') {


        $pubKey= $this->alipayrsaPublicKey;
        $res = "-----BEGIN PUBLIC KEY-----\n" .
            wordwrap($pubKey, 64, "\n", true) .
            "\n-----END PUBLIC KEY-----";

        ($res) or die('支付宝RSA公钥错误。请检查公钥文件格式是否正确');  

        //调用openssl内置方法验签，返回bool值

        if ("RSA2" == $signType) {
            $result = (bool)openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256);
        } else {
            $result = (bool)openssl_verify($data, base64_decode($sign), $res);
        }

        return $result;
    }


    /**
     * 组合业务参数生成 bizcontent
     * @param [type] $data [description]
     */
    private function setBizcontent($data) 
    {
        $params['body']             = $data['body'];//商品描述
        $params['subject']          = $data['subject'];//商品名称
        $params['out_trade_no']     = $data['order_sn'];//系统订单
        $params['timeout_express']  = $this->timeout_express;//过期时间
        $params['total_amount']     = $data['money'];//金额
        $params['product_code']     = ;    

        $bizcontent = json_encode($params, JSON_UNESCAPED_UNICODE);

        return $bizcontent;
    }

    /**
     * 将参数封装到 变量数组中
     * @param [type] $bizcontent [description]
     * @param string $signType   [description]
     */
    private function setAttributes($bizcontent, $signType = 'RSA') 
    {

        //组装参数
        $params['app_id']       = $this->appid;
        $params['method']       = $this->method;
        $params['format']       = $this->format; 
        $params['sign_type']    = $signType;
        $params['timestamp']    = date("Y-m-d H:i:s");
        $params['alipay_sdk']   = $this->alipaySdkVersion;
        $params['charset']      = $this->charset;
        $params['version']      = $this->apiVersion;
        $params['notify_url']   = $this->notify_url;
        $params['biz_content']  = $bizcontent;
        $params['encrypt_type'] = $this->encryptType;
        //检测是否有空值
        $this->checkEmpty($params);

        return $params;

    }

    /**
     * 获得签名
     * @param  [type] $params   [description]
     * @param  [type] $signType [description]
     * @return [type]           [description]
     */
    private function sign($params, $signType) {
        //讲参数组合长字符串
        $data    =  $this->getSignContent($params);

        $priKey  =  $this->rsaPrivateKey;
        $res     = "-----BEGIN RSA PRIVATE KEY-----\n" .
                    wordwrap($priKey, 64, "\n", true) .
                    "\n-----END RSA PRIVATE KEY-----";

        if ("RSA" == $signType ) {

            openssl_sign($data, $sign, $res);
        }

        if ("RSA2" == $signType ) {

            openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
        }

        $sign = base64_encode($sign);

        return $sign;

    }

    /**
     * 讲参数组合长字符串
     * @param  [type] $params [description]
     * @return [type]         [description]
     */
    protected function getSignContent($params) {

        $stringToBeSigned = "";

        $i = 0;
        foreach ($params as $k => $v) {

            if ($i == 0) {
                $stringToBeSigned .= "$k" . "=" . "$v";
            } else {
                $stringToBeSigned .= "&" . "$k" . "=" . "$v";
            }

            $i++;
        }
        unset ($k, $v);
        return $stringToBeSigned;
    }

    /**
     * 检查是否有空值
     * @param  [type] $params [description]
     * @return [type]         [description]
     */
    private function checkEmpty($params) {

        foreach ($variable as $key => $value) {

            if (!isset($value) || $value === null || trim($value) === "") {
                echo '参数缺失';
                die;
            }
        }

    }
    

    /** rsaCheckV1 & rsaCheckV2
     *  验证签名
     *  在使用本方法前，必须初始化AopClient且传入公钥参数。
     *  公钥是否是读取字符串还是读取文件，是根据初始化传入的值判断的。
     **/
    public function rsaCheckV1($params, $rsaPublicKeyFilePath,$signType='RSA') {
        $sign = $params['sign'];
        $params['sign_type'] = null;
        $params['sign'] = null;
        return $this->verify($this->getSignContent($params), $sign, $rsaPublicKeyFilePath,$signType);
    }

    public function rsaCheckV2($params, $rsaPublicKeyFilePath, $signType='RSA') {
        $sign = $params['sign'];
        $params['sign'] = null;
        return $this->verify($this->getSignContent($params), $sign, $rsaPublicKeyFilePath, $signType);
    }


}